package edu.udo.sopra10.chaturaji.gui;

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.regex.Pattern;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

import se.datadosen.component.RiverLayout;
import edu.udo.sopra10.chaturaji.controllers.SystemController;
import edu.udo.sopra10.chaturaji.entities.Player;
import edu.udo.sopra10.chaturaji.entities.transfer.TransferTerminationCondition;
import edu.udo.sopra10.chaturaji.gui.util.RegExpValidatorFocusListener;
import edu.udo.sopra10.chaturaji.util.PlayerUtil;

/**
 * Frame that functions as a game creation dialog. Allows to specify player
 * names, types and if applicable email addresses, and to select the game's
 * type deciding about its termination condition.
 * 
 * @author Patrick Westerhoff
 */
public class NewGameDialog extends JDialog implements ActionListener
{
	private static final long serialVersionUID = -5984567693612548675L;
	private static final int PADDING = 3;
	private static final int MARGIN = 15;
	private static final String[] playerTypes = new String[] { "Mensch", "E-Mail Spieler", "Computer Einfach",
			"Computer Normal", "Computer Schwer" };
	private static final Pattern emailPattern = Pattern.compile( "\\S+@\\S+\\.[a-z]{1,5}", Pattern.CASE_INSENSITIVE );
	
	/* default settings */
	private static final String DEFAULT_POINT_GOAL = "18";
	private static final String DEFAULT_ROUND_LIMIT = "23";
	
	/* game type settings */
	private ButtonGroup gameTypeOptionGroup;
	private JRadioButton pointGoalGameOption;
	private JRadioButton roundLimitGameOption;
	private JRadioButton singleEliminationGameOption;
	private JRadioButton totalEliminationGameOption;
	private JTextField pointGoalTextField;
	private JTextField roundLimitTextField;
	
	/* player settings */
	private PlayerSettingsPanel player1;
	private PlayerSettingsPanel player2;
	private PlayerSettingsPanel player3;
	private PlayerSettingsPanel player4;
	
	/* controls */
	private JButton abortButton;
	private JButton startButton;
	
	/**
	 * Constructs the NewGameFrame and initializes the interface.
	 * 
	 */
	public NewGameDialog ( JFrame parentFrame )
	{
		super( parentFrame, "Neues Spiel", true );
		
		JPanel mainPanel = new JPanel( new RiverLayout( PADDING, PADDING ) );
		mainPanel.setBorder( new EmptyBorder( 0, MARGIN, MARGIN, MARGIN ) );
		
		/* game type settings */
		pointGoalGameOption = new JRadioButton( "Punkteziel" );
		roundLimitGameOption = new JRadioButton( "Rundenlimit" );
		singleEliminationGameOption = new JRadioButton( "Einfacher Sieg" );
		totalEliminationGameOption = new JRadioButton( "Vollständiger Sieg" );
		
		// game type value text boxes
		pointGoalTextField = new JTextField( DEFAULT_POINT_GOAL, 4 );
		pointGoalTextField.setHorizontalAlignment( JTextField.RIGHT );
		roundLimitTextField = new JTextField( DEFAULT_ROUND_LIMIT, 4 );
		roundLimitTextField.setHorizontalAlignment( JTextField.RIGHT );
		
		// listener
		FocusListener numberFocusListener = new RegExpValidatorFocusListener( Pattern.compile( "\\d{1,3}" ) );
		pointGoalTextField.addFocusListener( numberFocusListener );
		roundLimitTextField.addFocusListener( numberFocusListener );
		pointGoalGameOption.addActionListener( this );
		roundLimitGameOption.addActionListener( this );
		singleEliminationGameOption.addActionListener( this );
		totalEliminationGameOption.addActionListener( this );
		
		// option group
		gameTypeOptionGroup = new ButtonGroup();
		gameTypeOptionGroup.add( pointGoalGameOption );
		gameTypeOptionGroup.add( roundLimitGameOption );
		gameTypeOptionGroup.add( singleEliminationGameOption );
		gameTypeOptionGroup.add( totalEliminationGameOption );
		
		// default settings
		pointGoalTextField.setEnabled( false );
		roundLimitTextField.setEnabled( false );
		singleEliminationGameOption.setSelected( true );
		
		// option panel
		JPanel optionPanel = new JPanel( new RiverLayout( 0, 0 ) );
		optionPanel.add( "", pointGoalGameOption );
		optionPanel.add( "tab", pointGoalTextField );
		optionPanel.add( "br", roundLimitGameOption );
		optionPanel.add( "tab", roundLimitTextField );
		optionPanel.add( "br", singleEliminationGameOption );
		optionPanel.add( "tab", totalEliminationGameOption );
		
		JLabel optionLabel = new JLabel( "Spieltyp:" );
		optionLabel.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
		mainPanel.add( "p vtop", optionLabel );
		mainPanel.add( "tab", optionPanel );
		
		/* players settings */
		player1 = new PlayerSettingsPanel( "Spieler 1", PlayerStatusPanel.getPlayerColor( (byte) 0 ) );
		player2 = new PlayerSettingsPanel( "Spieler 2", PlayerStatusPanel.getPlayerColor( (byte) 1 ) );
		player3 = new PlayerSettingsPanel( "Spieler 3", PlayerStatusPanel.getPlayerColor( (byte) 2 ) );
		player4 = new PlayerSettingsPanel( "Spieler 4", PlayerStatusPanel.getPlayerColor( (byte) 3 ) );
		
		mainPanel.add( "p", player1 );
		mainPanel.add( "br", player2 );
		mainPanel.add( "br", player3 );
		mainPanel.add( "br", player4 );
		
		/* controls */
		abortButton = new JButton( "Abbrechen" );
		startButton = new JButton( "Spiel starten" );
		
		abortButton.addActionListener( new ActionListener()
			{
				@Override
				public void actionPerformed ( ActionEvent e )
				{
					NewGameDialog.this.setVisible( false );
				}
			} );
		startButton.addActionListener( this );
		
		mainPanel.add( "p center", abortButton );
		mainPanel.add( "center", startButton );
		
		/* global */
		this.setContentPane( mainPanel );
		this.setResizable( false );
		this.pack();
	}
	
	@Override
	public void actionPerformed ( ActionEvent event )
	{
		// start new game button
		if ( event.getSource().equals( startButton ) )
		{
			if ( player1.getFullName() == null || player2.getFullName() == null || player3.getFullName() == null
					|| player4.getFullName() == null )
			{
				JOptionPane.showMessageDialog( this, "E-Mail Adresse bei E-Mail Spieler fehlt.", "Fehler",
						JOptionPane.ERROR_MESSAGE );
				return;
			}
			
			this.dispose();
			
			// create the game
			if ( pointGoalGameOption.isSelected() )
			{
				SystemController.newGame( player1.getFullName(), player1.getPlayerType(), player2.getFullName(),
						player2.getPlayerType(), player3.getFullName(), player3.getPlayerType(), player4.getFullName(),
						player4.getPlayerType(), TransferTerminationCondition.SCORE_LIMIT, Integer
								.parseInt( pointGoalTextField.getText() ) );
			}
			else if ( roundLimitGameOption.isSelected() )
			{
				SystemController.newGame( player1.getFullName(), player1.getPlayerType(), player2.getFullName(),
						player2.getPlayerType(), player3.getFullName(), player3.getPlayerType(), player4.getFullName(),
						player4.getPlayerType(), TransferTerminationCondition.ROUND_LIMIT, Integer
								.parseInt( roundLimitTextField.getText() ) );
			}
			else if ( singleEliminationGameOption.isSelected() )
			{
				SystemController.newGame( player1.getFullName(), player1.getPlayerType(), player2.getFullName(),
						player2.getPlayerType(), player3.getFullName(), player3.getPlayerType(), player4.getFullName(),
						player4.getPlayerType(), TransferTerminationCondition.ELIMINATION );
			}
			else if ( totalEliminationGameOption.isSelected() )
			{
				SystemController.newGame( player1.getFullName(), player1.getPlayerType(), player2.getFullName(),
						player2.getPlayerType(), player3.getFullName(), player3.getPlayerType(), player4.getFullName(),
						player4.getPlayerType(), TransferTerminationCondition.TOTAL_ELIMINATION );
			}
		}
		
		// update game type options
		pointGoalTextField.setEnabled( pointGoalGameOption.isSelected() );
		roundLimitTextField.setEnabled( roundLimitGameOption.isSelected() );
	}
	
	
	/**
	 * Custom panel for prompting for a player's settings, namely the player's
	 * name, the type and the email address if applicable. The settings are
	 * displayed next to a color panel showing the player's color.
	 * 
	 * @author Patrick Westerhoff
	 * 
	 */
	private class PlayerSettingsPanel extends JPanel implements FocusListener, ItemListener
	{
		private static final long serialVersionUID = -8476052583514600496L;
		
		private String defaultMail = "E-Mail Adresse";
		private String defaultName;
		private JTextField playerName;
		private JTextField playerMail;
		private JComboBox playerType;
		
		/**
		 * Constructs a settings panel.
		 * 
		 * @param defaultName default name to be displayed.
		 * @param playerColor color associated to the player.
		 */
		public PlayerSettingsPanel ( String defaultName, Color playerColor )
		{
			this.defaultName = defaultName;
			
			playerName = new JTextField( defaultName );
			playerName.addFocusListener( this );
			playerName.setForeground( Color.GRAY );
			
			playerMail = new JTextField( defaultMail );
			playerMail.addFocusListener( this );
			playerMail.addFocusListener( new RegExpValidatorFocusListener( emailPattern ) );
			playerMail.setForeground( Color.GRAY );
			playerMail.setVisible( false );
			
			playerType = new JComboBox( playerTypes );
			playerType.setEditable( false );
			playerType.addItemListener( this );
			
			this.setLayout( new GridLayout( 1, 3, PADDING, PADDING ) );
			this.setBorder( BorderFactory.createMatteBorder( 0, 10, 0, 0, playerColor ) );
			this.add( playerName );
			this.add( playerType );
			this.add( playerMail );
		}
		
		/**
		 * Returns the entered player's name. If the name was not set by the
		 * user, a randomly generated one is chosen and the text field is
		 * updated.
		 * 
		 * @return player name.
		 */
		public String getPlayerName ()
		{
			if ( playerName.getText().equals( defaultName ) )
			{
				playerName.setForeground( null );
				playerName.setText( PlayerUtil.getRandomName() );
			}
			
			return playerName.getText();
		}
		
		/**
		 * Returns the entered email address if the player type is EMAIL, null
		 * if the email address is invalid or empty. The returned email address
		 * is in a valid email client syntax with the player name as the
		 * display name.
		 * 
		 * @return email address or null if the address is invalid.
		 */
		public String getPlayerMail ()
		{
			if ( getPlayerType() != Player.Type.EMAIL || playerMail.getText().equals( defaultMail ) )
				return null;
			
			return getPlayerName() + "<" + playerMail.getText() + ">";
		}
		
		/**
		 * Returns the correct and full name based on the player's type.
		 * Returns null if the email address is invalid or missing. Returns the
		 * correct email address if the player type is EMAIL, otherwise returns
		 * just the name.
		 * 
		 * @return the full name.
		 */
		public String getFullName ()
		{
			if ( getPlayerType() == Player.Type.EMAIL )
			{
				if ( getPlayerMail() == null )
					return null;
				return getPlayerMail();
			}
			else
				return getPlayerName();
		}
		
		/**
		 * Returns the player type, the user selected.
		 * 
		 * @return the selected player type.
		 */
		public Player.Type getPlayerType ()
		{
			switch ( playerType.getSelectedIndex() )
			{
				case 0:
					return Player.Type.HUMAN;
				case 1:
					return Player.Type.EMAIL;
				case 2:
					return Player.Type.AI_EASY;
				case 3:
					return Player.Type.AI_MEDIUM;
				case 4:
					return Player.Type.AI_HARD;
				default:
					playerType.setSelectedIndex( 0 );
					return Player.Type.HUMAN;
			}
		}
		
		@Override
		public void focusGained ( FocusEvent event )
		{
			JTextField source = (JTextField) event.getSource();
			source.setForeground( null );
			source.setText( "" );
		}
		
		@Override
		public void focusLost ( FocusEvent event )
		{
			String defaultText = event.getSource().equals( playerName ) ? this.defaultName : defaultMail;
			JTextField source = (JTextField) event.getSource();
			
			if ( source.getText().equals( "" ) )
			{
				source.setForeground( Color.GRAY );
				source.setText( defaultText );
			}
			else
			{
				source.removeFocusListener( this );
				defaultText = "";
			}
		}
		
		@Override
		public void itemStateChanged ( ItemEvent e )
		{
			if ( getPlayerType() == Player.Type.EMAIL )
				playerMail.setVisible( true );
			else
				playerMail.setVisible( false );
		}
	}
}
